﻿using System;
using System.Collections.Specialized;
using Xunit;
using XunitExt;

namespace Rarous.NForms.Test {
  public class FormItemsTests {

    private Form _form;

    /// <summary>
    /// Initializes a new instance of the FormItemsTests class.
    /// </summary>
    public FormItemsTests() {
      _form = new Form();
    }

    private TextItem GetTextInput() {
      return new TextItem(_form, "text_input", "test");
    }

    private TextAreaItem GetTextArea() {
      return new TextAreaItem(_form, "area", "text", 12, 22);
    }

    [Fact]
    public void TextItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new TextItem(null, "test", "test"));
    }

    [Fact]
    public void TextItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new TextItem(_form, null, "test"));
      Assert.Throws<ArgumentException>(() => new TextItem(_form, String.Empty, "test"));
    }

    [Fact]
    public void TextItem_should_render_empty_label_when_not_specified() {
      var input = new TextItem(_form, "text_input", null);

      input.Label.ToString().ShouldEqual(String.Empty);
    }

    [Fact]
    public void TextItem_constructor_should_register_item_in_parent_form() {
      var input = GetTextInput();

      _form["text_input"].ShouldBeSameAs(input);
    }

    [Fact]
    public void TextItem_should_have_label_and_control() {
      var input = GetTextInput();

      input.Control.ShouldNotBeNull();
      input.Label.ShouldNotBeNull();
    }

    [Fact]
    public void TextItem_should_render_password_input_when_secure_specified() {
      var input = new TextItem(_form, "text_input", "test", null, true);

      Assert.Equal(0, String.Compare("password", input.Control["Type"].ToString(), true));
    }

    [Fact]
    public void TextItem_required_should_add_required_class() {
      var input =  GetTextInput().Required("povinne");

      input.Control.HasClass("required").ShouldBeTrue();
    }

    [Fact]
    public void TextItem_contructor_should_set_control_attributes() {
      var input = new TextItem(_form, "text_input", "text", new { Class = "someClass" });

      input.Control.HasClass("someClass").ShouldBeTrue();
    }

    [Fact]
    public void FormItem_should_set_control_attribute_via_method() {
      var input = GetTextInput().SetAttribute("Title", "some text");

      input.Control["Title"].ShouldEqual("some text");
    }

    [Fact]
    public void FormItem_should_render_value() {
      var input = GetTextInput().SetValue("test");

      input.Control["Value"].ShouldEqual("test");
    }

    [Fact]
    public void FormItem_should_render_empty_value() {
      var input = GetTextInput().SetEmptyValue("http://");

      input.Control["Value"].ShouldEqual("http://");
    }

    [Fact]
    public void FormItem_should_return_empty_string_after_setting_empty_value() {
      var input = GetTextInput().SetEmptyValue("http://");

      input.GetValue().ShouldEqual(String.Empty);
    }

    [Fact]
    public void FormItem_should_have_id() {
      var input = GetTextInput();

      input.Control["Id"].ShouldNotBeNull();
    }

    [Fact]
    public void NumericItem_should_render_numeric_CSS_class() {
      var input = new NumericItem(_form, "test", "test");

      input.Control.HasClass("numeric").ShouldBeTrue();
    }

    [Fact]
    public void HiddenItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new HiddenItem(null, "test"));
    }

    [Fact]
    public void HiddenItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new HiddenItem(_form, null));
      Assert.Throws<ArgumentException>(() => new HiddenItem(_form, String.Empty));
    }

    [Fact]
    public void HiddenItem_should_render_with_value() {
      var hidden = new HiddenItem(_form, "test", new { Value = "test" });

      hidden.Control["Value"].ShouldEqual("test");

      var hidden2 = new HiddenItem(_form, "test").SetValue("test");

      hidden2.Control["Value"].ShouldEqual("test");
    }

    [Fact]
    public void TextAreaItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new TextAreaItem(null, "test", "test", 0, 0));
    }

    [Fact]
    public void TextAreaItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new TextAreaItem(_form, null, "test", 0, 0));
      Assert.Throws<ArgumentException>(() => new TextAreaItem(_form, String.Empty, "test", 0, 0));
    }

    [Fact]
    public void TextAreaItem_should_render_empty_label_when_not_specified() {
      var input = new TextAreaItem(_form, "text_input", null, 0, 0);

      input.Label.ToString().ShouldEqual(String.Empty);
    }

    [Fact]
    public void TextAreaItem_constructor_should_set_area_dimensions() {
      var area = GetTextArea();

      area.Control["Rows"].ShouldEqual("12");
      area.Control["Cols"].ShouldEqual("22");
    }

    [Fact]
    public void TextAreaItem_SetValue_should_set_control_text_property() {
      var area = GetTextArea().SetValue("some text");

      area.Control.Text.ShouldEqual("some text");
    }

    [Fact]
    public void TextAreaItem_required_should_add_required_class() {
      var input = GetTextArea().Required("povinne");

      input.Control.HasClass("required").ShouldBeTrue();
    }

    [Fact]
    public void RadioItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new RadioItem(null, "test", "test", new ListItem[0], null));
    }

    [Fact]
    public void RadioItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new RadioItem(_form, null, "test", new ListItem[0], null));
      Assert.Throws<ArgumentException>(() => new RadioItem(_form, String.Empty, "test", new ListItem[0], null));
    }

    [Fact]
    public void RadioItem_should_throw_with_null_items_collection() {
      Assert.Throws<ArgumentNullException>(() => new RadioItem(_form, "test", "test", null, null));
    }

    [Fact]
    public void RadioItem_null_atributes_object_should_render_no_wrapper_tag() {
      var radio = new RadioItem(_form, "test", "test", new ListItem[0], null);

      radio.Control.OpeningTag.ShouldEqual(String.Empty);
      radio.Control.ClosingTag.ShouldEqual(String.Empty);
    }

    [Fact]
    public void RadioItem_atributes_object_should_render_div_wrapper_tag() {
      var radio = new RadioItem(_form, "test", "test", new ListItem[0], new { Class = "test" });

      radio.Control.OpeningTag.ShouldEqual("<div class=\"test\">");
      radio.Control.ClosingTag.ShouldEqual("</div>");
    }

    [Fact]
    public void RadioItem_should_render_all_given_items() {
      const int ElementsPerItem = 3;
      var radio = new RadioItem(_form, "test", "test", new ListItem[] {
        new ListItem { Value = 1, Text = "Item1" },
        new ListItem { Value = 2, Text = "Item2" }
      });

      radio.Control.ChildrenCount.ShouldEqual(2 * ElementsPerItem);
    }

    [Fact]
    public void RadioItem_should_select_item() {
      var radio = new RadioItem(_form, "test", "test", new ListItem[] {
        new ListItem { Value = 1, Text = "Item1" },
        new ListItem { Value = 2, Text = "Item2" }
      });

      radio.SetValue(1);

      radio.Control[0].Attributes["Selected"].ShouldEqual("selected");
    }

    [Fact]
    public void SelectItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new SelectItem(null, "test", "test", new ListItem[0], null));
    }

    [Fact]
    public void SelectItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new SelectItem(_form, null, "test", new ListItem[0], null));
      Assert.Throws<ArgumentException>(() => new SelectItem(_form, String.Empty, "test", new ListItem[0], null));
    }

    [Fact]
    public void SelectItem_should_throw_with_null_items_collection() {
      Assert.Throws<ArgumentNullException>(() => new SelectItem(_form, "test", "test", null, null));
    }

    [Fact]
    public void SelectItem_should_render_all_given_items() {
      var radio = new SelectItem(_form, "test", "test", new ListItem[] {
        new ListItem { Value = 1, Text = "Item1" },
        new ListItem { Value = 2, Text = "Item2" }
      });

      radio.Control.ChildrenCount.ShouldEqual(2);
    }

    [Fact]
    public void SelectItem_should_select_item() {
      var select = new SelectItem(_form, "test", "test", new ListItem[] { 
        new ListItem { Value = 1, Text = "Item1" },
        new ListItem { Value = 2, Text = "Item2" }
      });

      select.SetValue(1);

      select.Control[0].Attributes["Selected"].ShouldEqual("selected");
    }

    [Fact]
    public void SelectItem_should_select_more_items() {
      var select = new SelectItem(_form, "test", "test", new ListItem[] { 
        new ListItem { Value = 1, Text = "Item1" },
        new ListItem { Value = 2, Text = "Item2" }
      });

      select.SetValues(new object[] { 1, 2 });

      select.Control[0].Attributes["Selected"].ShouldEqual("selected");
      select.Control[1].Attributes["Selected"].ShouldEqual("selected");
    }

    [Fact]
    public void ButtonItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new ButtonItem(null, "test"));
    }

    [Fact]
    public void ButtonItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new ButtonItem(_form, null));
      Assert.Throws<ArgumentException>(() => new ButtonItem(_form, String.Empty));
    }

    [Fact]
    public void ButtonItem_should_change_button_type() {

      new ButtonItem(_form, "button").Control["Type"].ShouldEqual("Button");

      new ButtonItem(_form, "image_button", null, InputType.Image).Control["Type"].ShouldEqual("Image");

      new ButtonItem(_form, "submit", null, InputType.Submit).Control["Type"].ShouldEqual("Submit");

      new ButtonItem(_form, "reset", null, InputType.Reset).Control["Type"].ShouldEqual("Reset");

    }

    [Fact]
    public void CheckBoxItem_should_throw_with_null_form_in_constructor() {
      Assert.Throws<ArgumentNullException>(() => new CheckBoxItem(null, "test", "test", null));
    }

    [Fact]
    public void CheckBoxItem_should_throw_with_null_or_empty_name() {
      Assert.Throws<ArgumentException>(() => new CheckBoxItem(_form, null, "test", null));
      Assert.Throws<ArgumentException>(() => new CheckBoxItem(_form, String.Empty, "test", null));
    }

    [Fact]
    public void CheckBoxItem_should_render_empty_label_when_not_specified() {
      var input = new CheckBoxItem(_form, "check_input", null, null);

      input.Label.ToString().ShouldEqual(String.Empty);
    }

  }
}
